import hashlib
import json
import requests
from time import time
from urllib.parse import urlparse
from ecdsa import VerifyingKey, SigningKey


class BlockChain(object):
    def __init__(self):
        self.nodes = set()
        self.chain = []
        self.transactions_pool = []
        self.transactions_package = []
        self.user = []
        self.transaction_id = 0
        self.signing_key = SigningKey.generate()

        # 创建创世区块
        self.new_block(previous_hash=0, proof=0)
        self.register_user("0", self.signing_key.get_verifying_key().to_string())

    def register_node(self, address):
        """
        在节点列表中添加一个新节点
        """
        prsed_url = urlparse(address)
        self.nodes.add(prsed_url.netloc)

    def register_user(self, account, vk):
        """"
        注册一个新账户
        """
        new_user = {
            'account': account,
            "verifying_key": vk
        }
        self.user.append(new_user)

    def valid_chain(self, chain):
        """
        确定一个给定的区块链是否有效
        """
        last_block = chain[0]
        current_index = 1

        while current_index < len(chain):
            block = chain[current_index]
            print(f'{last_block}')
            print(f'{block}')
            print("\n______\n")
            # 检查block的散列是否正确
            if block['previous_hash'] != self.hash(last_block):
                return False
            # 检查工作证明是否正确
            if not self.valid_proof(last_block['proof'], block['proof']):
                return False

            last_block = block
            current_index += 1
        return True

    def resolve_conflicts(self):
        """
        尝试从节点网络更新本地区块链
        """
        neighbours = self.nodes
        new_chain = None
        # 寻找最长链条
        max_length = len(self.chain)

        # 获取并验证网络中的所有节点的链
        for node in neighbours:
            response = requests.get(f'http://{node}/chain')

            if response.status_code == 200:
                length = response.json()['length']
                chain = response.json()['chain']

                # 检查长度是否长，链是否有效
                if length > max_length and self.valid_chain(chain):
                    max_length = length
                    new_chain = chain

        # 如果发现一个新的有效链比当前的长，就替换当前的链
        if new_chain:
            self.chain = new_chain
            return True
        return False

    def new_block(self, proof, previous_hash=None):
        """
        出块
        """
        if (previous_hash is not None):
            block = {
                'index': len(self.chain),
                'timestamp': time(),
                'transactions': self.transactions_package,
                'proof': proof,
                'previous_hash': previous_hash
            }
        else:
            block = {
                'index': len(self.chain),
                'timestamp': time(),
                'transactions': self.transactions_package,
                'proof': proof,
                'previous_hash': self.hash(self.last_block())
            }

        # 重置当前交易包
        self.transactions_package=[]
        self.chain.append(block)
        return block

    def valid_transaction(self, index):
        if (index >= len(self.transactions_pool)):
            return False
        ct = {
            'transaction_id': self.transactions_pool[index]['transaction_id'],
            'sender': self.transactions_pool[index]['sender'],
            'recipient': self.transactions_pool[index]['recipient'],
            'amount': self.transactions_pool[index]['amount'],
        }
        for i in range(len(self.transactions_pool)):
            if (i != index and self.transactions_pool[i]['transaction_id'] == ct['transaction_id']):
                return False
        found, acc = self.search_account(ct['sender'])
        if (found is False):
            return False
        vk = VerifyingKey.from_string(self.user[acc]['verifying_key'])
        #return vk.verify(self.transactions_pool[index]['verifying_code'], json.dumps(ct, sort_keys=True).encode())
        return True

    def search_account(self, account):
        for i in range(len(self.user)):
            if (account == self.user[i]['account']):
                return True, i
        return False, 0

    def execute_transactions(self):
        """
        执行交易并打包
        """
        for i in range(len(self.transactions_pool)):
            self.transactions_package.append({
                'transaction_id': self.transactions_pool[i]['transaction_id'],
                'sender': self.transactions_pool[i]['sender'],
                'recipient': self.transactions_pool[i]['recipient'],
                'amount': self.transactions_pool[i]['amount'],
            })
        self.transactions_pool = []
        return len(self.transactions_package)

    def new_transaction(self, sender, recipient, amount, verifying_code):
        # 将新事务添加到事务列表中
        self.transactions_pool.append({
            'transaction_id': self.transaction_id,
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
            'verifying_code': verifying_code
        })
        self.transaction_id += 1
        return len(self.transactions_pool) - 1

    @staticmethod
    def hash(block):
        """
        给一个区块生成 SHA-256 值
        """
        # 必须确保这个字典（区块）是经过排序的，否则将会得到不一致的散列
        block_string = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(block_string).hexdigest()

    @property
    def last_block(self):
        # 返回链中的最后一个块
        return self.chain[-1]

    def proof_of_work(self, last_proof):
        # 工作算法的简单证明
        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1
        return proof

    @staticmethod
    def valid_proof(last_proof, proof):
        # 验证证明
        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:4] == "0000"
